package com.base.springboot.starter.common.http;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.config.ConnectionConfig;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.util.Objects;

/**
 * 这个类的目的是构造自定义连接池管理器
 * 构造过程参见 HttpClientBuilder 947到1004行代码，剪裁了从系统变量读取配置的部分
 * 为什么要自己构建而不用上述代码？因为HttpClient没有对外暴露获取这个cm对象的接口，无法对cm对象的添加监控逻辑，或者后续其它自定义行为
 *
 * User: wangwei
 * Date: 2021-05-18 2:16 下午
 */
@Slf4j
public class PoolingHttpClientConnectionManagerBuilder {

    private SocketConfig defaultSocketConfig;
    private ConnectionConfig defaultConnectionConfig;
    private CsHttpClientConfig csHttpClientConfig = new CsHttpClientConfig.Builder().build();

    public final PoolingHttpClientConnectionManagerBuilder setDefaultSocketConfig(final SocketConfig config) {
        this.defaultSocketConfig = config;
        return this;
    }

    public final PoolingHttpClientConnectionManagerBuilder setDefaultConnectionConfig(final ConnectionConfig config) {
        this.defaultConnectionConfig = config;
        return this;
    }

    public final PoolingHttpClientConnectionManagerBuilder setCSConfig(final CsHttpClientConfig config) {
        Objects.requireNonNull(config);
        this.csHttpClientConfig = config;
        return this;
    }

    public PoolingHttpClientConnectionManager build() {

        LayeredConnectionSocketFactory sslSocketFactory = getLayeredConnectionSocketFactory();
        PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
                RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("http", PlainConnectionSocketFactory.getSocketFactory())
                        .register("https", sslSocketFactory)
                        .build(),
                null,
                null,
                null,
                csHttpClientConfig.getConnTimeToLive(),
                csHttpClientConfig.getConnTimeToLiveTimeUnit());

        if (defaultSocketConfig != null) {
            poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
        }
        if (defaultConnectionConfig != null) {
            poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
        }
        if (csHttpClientConfig.getMaxConnTotal() > 0) {
            poolingmgr.setMaxTotal(csHttpClientConfig.getMaxConnTotal());
        }
        if (csHttpClientConfig.getMaxConnPerRoute() > 0) {
            poolingmgr.setDefaultMaxPerRoute(csHttpClientConfig.getMaxConnPerRoute());
        }

        if (csHttpClientConfig.getValidateAfterInactivityInMs() > 0) {
            poolingmgr.setValidateAfterInactivity(csHttpClientConfig.getValidateAfterInactivityInMs());
        }

        return poolingmgr;
    }

    private LayeredConnectionSocketFactory getLayeredConnectionSocketFactory() {

        final String[] supportedProtocols = null;
        final String[] supportedCipherSuites = null;

        //主机名验证
        HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(null);

        SSLContext sslContext = getSSLContext();

        if (sslContext != null) {
            return new SSLConnectionSocketFactory(
                    sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
        } else {
            return new SSLConnectionSocketFactory(
                    SSLContexts.createDefault(),
                    hostnameVerifier);
        }
    }

    /**
     * 设置ssl上下文
     * http://java.sun.com/j2se/1.5.0/docs/guide/security/jsse/JSSERefGuide.html
     * https://blog.csdn.net/u011179993/article/details/50518349
     * @return
     */
    private SSLContext getSSLContext() {
        //KeyStore myTrustStore = ...
        //return SSLContexts.custom().loadTrustMaterial(myTrustStore).build();
        return null;
    }
}
